﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Features2D;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using OpenCvApplication.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OpenCvApplication.View
{
    public partial class FrmCircleDetection : FrmBase
    {
        private readonly FrmMain? _frmMain1;
        private volatile bool _isRun = false;
        public FrmCircleDetection()
        {
            InitializeComponent();

            TraverseControls(this);
        }
        private void TraverseControls(Control control)
        {
            foreach (Control childControl in control.Controls)
            {
                // 处理当前控件
                // TODO: 在这里对控件进行需要的操作
                if (childControl is TrackBar trackBar)
                    trackBar.Scroll += TrackBar_Scroll;
                if (childControl is NumericUpDown numericUp)
                    numericUp.ValueChanged += NumericUp_ValueChanged;
                // 递归处理子控件
                TraverseControls(childControl);
            }
        }

        private void NumericUp_ValueChanged(object? sender, EventArgs e)
        {
            NumericUpDown numericUp = (NumericUpDown)sender;
            switch (numericUp.Name)
            {
                case "numericUpDown2":
                    lblsigmaY.Text = GetValue(trackBar3, numericUp).ToString("0.00");
                    break;
                case "numericUpDown1":
                    label10.Text = GetValue(trackBar4, numericUp).ToString("0.00");
                    break;
                case "numericUpDown3":
                    label12.Text = GetValue(trackBar5, numericUp).ToString("0.00");
                    break;
                case "numericUpDown4":
                    label14.Text = GetValue(trackBar6, numericUp).ToString("0.00");
                    break;
            }
        }

        private void TrackBar_Scroll(object? sender, EventArgs e)
        {
            TrackBar trackBar = sender as TrackBar;
            switch (trackBar.Name)
            {
                case "trackBar1":
                    label4.Text = trackBar.Value.ToString();
                    break;
                case "trackBar2":
                    label5.Text = trackBar.Value.ToString();
                    break;
                case "trackBar3":
                    lblsigmaY.Text = GetValue(trackBar, numericUpDown2).ToString("0.00");
                    break;
                case "trackBar4":
                    label10.Text = GetValue(trackBar, numericUpDown1).ToString("0.00");
                    break;
                case "trackBar5":
                    label12.Text = GetValue(trackBar, numericUpDown3).ToString("0.00");
                    break;
                case "trackBar6":
                    label14.Text = GetValue(trackBar, numericUpDown4).ToString("0.00");
                    break;
                case "trackBar7":
                    label16.Text = trackBar.Value.ToString();
                    break;
                case "trackBar8":
                    label18.Text = trackBar.Value.ToString();
                    break;
                case "trackBar9":
                    label20.Text = trackBar.Value.ToString();
                    break;
            }
        }
        private double GetValue(TrackBar trackBar, NumericUpDown numericUp)
        {
            return trackBar.Value + (float)numericUp.Value;
        }
        public FrmCircleDetection(FrmMain frmMain) : this()
        {
            _frmMain1 = frmMain;
            _frmMain1.Start = Start;
            Binds();
        }
        private void Binds()
        {
            BindType(typeof(ChainApproxMethod), this.comboBox1, "ChainApproxSimple");
            BindType(typeof(RetrType), this.comboBox2, "List");
            BindType(typeof(HoughType), this.comboBox3, "Gradient");
            BindType(typeof(TemplateMatchingType), this.comboBox4, "CcoeffNormed");
        }
        private void BindType(Type type, ComboBox comboBox, string defaultValue)
        {
            var enumValues = Enum.GetValues(type);
            var list = new List<IdValues>();
            int index = 0, curIndex = 0;
            foreach (Enum value in enumValues)
            {
                int hc = value.GetHashCode();
                list.Add(new IdValues
                {
                    Id = hc.ToString(),
                    Value = value.ToString(),
                    Standby = hc
                });
                if (value.ToString() == defaultValue)
                    index = curIndex;

                curIndex++;
            }

            comboBox.ValueMember = "Id";
            comboBox.DisplayMember = "Value";
            comboBox.DataSource = list;
            comboBox.SelectedIndex = index;
        }
        public void Start()
        {
            var files = _frmMain1.Files;
            if (files == null || files.Length == 0)
            {
                MessageBox.Show("无图片");
                return;
            }
            if (_isRun)
            {
                MessageBox.Show("正在运行");
                return;
            }
            _isRun = true;
            _frmMain1.ProgressBarMaxValue = files.Length;
            _frmMain1.ProgressBarValue = 0;
            _frmMain1.ClearMessage();
            Task.Run(() =>
             {
                 CircleDetection(files);
             });
        }
        Image<Bgr, byte> image = null;
        Image<Gray, byte> cannyEdges = null;
        VectorOfVectorOfPoint contours = null;
        Image<Bgr, byte> nimage = null;
        Image<Gray, byte> gimage = null;
        const int centerRadius = 6; // 圆心半径
        Image<Bgr, byte> templateImage;
        private void CircleDetection(string[] files)
        {
            try
            {
                for (int i = 0; i < files.Length; i++)
                {
                    contours?.Dispose();
                    cannyEdges?.Dispose();
                    nimage?.Dispose();
                    gimage?.Dispose();
                    templateImage?.Dispose();
                    image?.Dispose();


                    var file = files[i];
                    // 加载图像
                    image = new Image<Bgr, byte>(file);
                    nimage = image.Clone();
                    gimage = image.Convert<Gray, byte>();

                    bool isBinarization = false;
                    this.cbBinarization.ExecInvoke(() =>
                    {
                        isBinarization = cbBinarization.Checked;
                    });
                    if (isBinarization)
                    {

                        double thresholdValue = 128; // 设置二值化阈值
                        trackBar9.ExecInvoke(() =>
                        {
                            thresholdValue = trackBar9.Value;
                        });
                        double maxValue = 255; // 设置最大像素值
                        CvInvoke.Threshold(gimage, gimage, thresholdValue, maxValue, ThresholdType.Binary);



                        // 计算OTSU阈值
                        //var threshold = CvInvoke.Threshold(gimage, gimage, 0, 255, ThresholdType.BinaryInv | ThresholdType.Otsu);
                        //// 二值化图像
                        //gimage = gimage.ThresholdBinary(new Gray(threshold), new Gray(255));
                    }

                    // 对图像进行预处理，如降噪、平滑等
                    gimage._SmoothGaussian(5);

                    Stopwatch sw = Stopwatch.StartNew();
                    // 将图像转换为灰度图像
                    //Image<Gray, byte> grayImage = image.Convert<Gray, byte>();
                    switch (_selectIndex)
                    {
                        case 0:
                            FindContours(i);
                            break;
                        case 1:
                            HoughCircles(i);
                            break;
                        case 2:
                            MatchTemplate();
                            break;
                    }

                    sw.Stop();

                    _frmMain1.ShowImageAfter = (Bitmap)nimage.Bitmap.Clone();
                    _frmMain1.ShowImageAfter2 = (Bitmap)gimage.Bitmap.Clone();
                    _frmMain1.SetShowTime = sw.ElapsedMilliseconds;
                    _frmMain1.ProgressBarValue = (i + 1);
                    _frmMain1.ShowImageAgo = (Bitmap)image.Bitmap.Clone();
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
            finally
            {
                _isRun = false;
                Thread.Sleep(50);
            }
        }
        private void FindContours(int index)
        {
            RetrType retrType = RetrType.List;
            this.comboBox2.ExecInvoke(() =>
            {
                retrType = (RetrType)(this.comboBox2.Items[comboBox2.SelectedIndex] as IdValues).Standby;
            });
            ChainApproxMethod approxMethod = ChainApproxMethod.ChainApproxSimple;
            this.comboBox1.ExecInvoke(() =>
            {
                approxMethod = (ChainApproxMethod)(this.comboBox1.Items[comboBox1.SelectedIndex] as IdValues).Standby;
            });
            int h = 100;
            trackBar1.ExecInvoke(() =>
            {
                h = trackBar1.Value;
            });
            int w = 50;
            trackBar1.ExecInvoke(() =>
            {
                w = trackBar2.Value;
            });

            // 检测图像中的轮廓
            cannyEdges = gimage.Canny(h, w);
            contours = new VectorOfVectorOfPoint();
            CvInvoke.FindContours(cannyEdges, contours, null, retrType, approxMethod);

            // 针对每个轮廓应用最小二乘圆拟合算法
            for (int j = 0; j < contours.Size; j++)
            {
                using (VectorOfPoint contour = contours[j])
                {
                    // 拟合圆
                    CircleF circle = CvInvoke.MinEnclosingCircle(contour);
                    // 绘制圆
                    nimage.Draw(circle, new Bgr(Color.Red), 2);
                    // 绘制圆心
                    MCvScalar centerColor = new MCvScalar(0, 0, 255); // 蓝色
                    CvInvoke.Circle(nimage, circle.Center.PointFToPoint(), centerRadius, centerColor, -1); // 绘制实心圆心
                    _frmMain1.AddMessage = $"X={circle.Center.X},Y={circle.Center.Y},Radius={circle.Radius}";
                }
            }
            if (contours.Size == 0)
                _frmMain1.AddMessage = $"{index}未识别到圆";
        }
        private void HoughCircles(int index)
        {
            HoughType houghType = HoughType.Gradient;
            this.comboBox3.ExecInvoke(() =>
            {
                houghType = (HoughType)(comboBox3.Items[comboBox3.SelectedIndex] as IdValues).Standby;
            });
            double dp = 1D;
            double minDist = 20D;
            double param1 = 100;
            double param2 = 100;
            int minRadius = 0;
            int maxRadius = 0;
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(() =>
                {
                    dp = trackBar3.Value + (float)numericUpDown2.Value;
                    minDist = trackBar4.Value + (float)numericUpDown1.Value;
                    param1 = trackBar5.Value + (float)numericUpDown3.Value;
                    param2 = trackBar6.Value + (float)numericUpDown4.Value;
                    minRadius = trackBar7.Value;
                    maxRadius = trackBar8.Value;
                }));
            }
            else
            {
                dp = trackBar3.Value + (float)numericUpDown2.Value;
                minDist = trackBar4.Value + (float)numericUpDown1.Value;
                param1 = trackBar5.Value + (float)numericUpDown3.Value;
                param2 = trackBar6.Value + (float)numericUpDown4.Value;
                minRadius = trackBar7.Value;
                maxRadius = trackBar8.Value;
            }
            // 创建一个用于腐蚀的结构元素
            using Mat element = CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(3, 3), new Point(-1, -1));

            // 对图像进行腐蚀操作
            //CvInvoke.Erode(gimage, gimage, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            // 对图像进行膨胀操作
            //CvInvoke.Dilate(gimage, gimage, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            // 开运算
            CvInvoke.MorphologyEx(gimage, gimage, MorphOp.Open, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
            // 对图像应用 Hough 变换圆检测算法
            CircleF[] circles = CvInvoke.HoughCircles(
                gimage,
                houghType, // Hough 变换类型
                dp, // 图像分辨率的倒数
                minDist, // 圆心之间的最小距离
                param1, // Canny 边缘检测的高阈值
                param2,
                minRadius, // 最小半径
                maxRadius// 最大半径
            );

            foreach (CircleF circle in circles)
            {
                nimage.Draw(circle, new Bgr(Color.Red), 2);

                // 绘制圆心
                MCvScalar centerColor = new MCvScalar(0, 0, 255); // 蓝色
                // 在图像上绘制圆
                CvInvoke.Circle(nimage, circle.Center.PointFToPoint(), centerRadius, centerColor, -1); // 绘制实心圆心
                //CvInvoke.Circle(gimage, circle.Center.PointFToPoint(), (int)Math.Round(circle.Radius,0), centerColor, -1); // 绘制实心圆心
                _frmMain1.AddMessage = $"X={circle.Center.X},Y={circle.Center.Y},Radius={circle.Radius}";
            }
            if (circles.Length == 0)
                _frmMain1.AddMessage = $"{index}未识别到圆";
        }
        private void MatchTemplate()
        {
            // 定义匹配方法
            TemplateMatchingType method = TemplateMatchingType.CcoeffNormed;
            this.comboBox4.ExecInvoke(() =>
            {
                method = (TemplateMatchingType)(comboBox4.Items[comboBox4.SelectedIndex] as IdValues).Standby;
            });
            string path = string.Empty;
            textBox1.ExecInvoke(() =>
            {
                path = textBox1.Text.Trim();
            });

            templateImage = new Image<Bgr, byte>(path);
            // 转换为灰度图像
            using Image<Gray, byte> graySource = image.Convert<Gray, byte>();
            using Image<Gray, byte> grayTemplate = templateImage.Convert<Gray, byte>();

            // 执行模板匹配
            using Image<Gray, float> result = graySource.MatchTemplate(grayTemplate, method);
            // 获取匹配结果的最大值和位置
            double maxValue = 0;
            double mminValue = 0;
            Point maxLocation = new Point();
            Point minLoc = new Point();
            CvInvoke.MinMaxLoc(result, ref mminValue, ref maxValue, ref minLoc, ref maxLocation);

            // 获取模板图像的宽度和高度
            int templateWidth = grayTemplate.Width;
            int templateHeight = grayTemplate.Height;

            // 在原始图像上绘制圆
            Point center = new Point(maxLocation.X + templateWidth / 2, maxLocation.Y + templateHeight / 2);
            int radius = Math.Max(templateWidth, templateHeight) / 2;
            CvInvoke.Circle(nimage, center, radius, new Bgr(0, 0, 255).MCvScalar, 2);
        }
        private void FrmCircleDetection_FormClosing(object sender, FormClosingEventArgs e)
        {
            contours?.Dispose();
            cannyEdges?.Dispose();
            image?.Dispose();
            nimage?.Dispose();
            gimage?.Dispose();
            templateImage?.Dispose();
            this.Dispose();
        }
        int _selectIndex = 0;
        private void tabControl1_Selected(object sender, TabControlEventArgs e)
        {
            _selectIndex = e.TabPageIndex;
        }
    }
}
